const ReactiveFlags = { IS_REACTIVE: '__v_isReactive' }
const isObject = (target) => target && typeof target === 'object'
const isFunction = (target) => typeof target === 'function'
const isReactive = (target) => target[ReactiveFlags.IS_REACTIVE]
const proxyMap = new WeakMap()
const TrackOpTypes = { GET: 'GET', SET: 'SET' }
const targetMap = new WeakMap()
// 表示当前激活的effect
let activeEffect = null

const trackEffect = (dep) => {
  const shouldTrack = !dep.has(activeEffect)
  if (shouldTrack) {
    // 双向记忆方便清除
    dep.add(activeEffect)
    activeEffect.deps.push(dep)
  }
}

/**
 * @desc 实现依赖收集
 */
const track = (target, type, key) => {
  if (!activeEffect) return

  let depsMap = targetMap.get(target)
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()))
  }

  let dep = depsMap.get(key)
  if (!dep) {
    depsMap.set(key, (dep = new Set()))
  }

  trackEffect(dep)
}

const triggerEffect = (deps) => {
  const effects = new Set(deps)
  for (const effect of effects) {
    if (effect !== activeEffect) {
      if (effect.scheduler) {
        effect.scheduler()
      } else {
        effect.run()
      }
    }
  }
}

/**
 * @desc 实现依赖触发
 */
const trigger = (target, type, key, value) => {
  const depsMap = targetMap.get(target)
  if (!depsMap) return

  const deps = depsMap.get(key)
  if (!deps) return

  triggerEffect(deps)
}

class ReactiveEffect {
  constructor(fn, scheduler) {
    this.fn = fn
    this.active = true
    this.parent = null
    this.deps = []
    this.scheduler = scheduler
  }

  run() {
    if (!this.active) return this.fn()

    try {
      // 将激活的effect 给父类
      this.parent = activeEffect
      activeEffect = this

      // 每次收集依赖之前 清空依赖
      cleanEffect(this)
      return this.fn()
    } finally {
      activeEffect = this.parent
    }
  }
}

/**
 * @desc 清除依赖
 */
const cleanEffect = (effect) => {
  const { deps } = effect
  if (deps.length <= 0) return

  for (let i = 0; i < deps.length; i++) {
    deps[i].delete(effect)
  }
  deps.length = 0
}

/**
 * @desc 实现effect
 */
const effectImpl = (fn, options = {}) => {
  const _effect = new ReactiveEffect(fn, options.scheduler)

  // effect 函数初期执行一次
  _effect.run()
  const runner = _effect.run.bind(_effect)
  runner.effect = _effect
  return runner
}

/**
 * @desc 实现reactive函数
 */
const reactiveImpl = (target) => {
  if (!isObject(target)) {
    console.warn(`${target} need is object`)
    return target
  }

  const existProxy = proxyMap.get(target)
  if (existProxy) return existProxy

  if (target[ReactiveFlags.IS_REACTIVE]) return target

  const proxy = new Proxy(target, {
    get: (target, key, receiver) => {
      if (key === ReactiveFlags.IS_REACTIVE) return true
      const res = Reflect.get(target, key, receiver)

      // 依赖收集
      track(target, TrackOpTypes.GET, key)

      return isObject(res) ? reactive(res) : res
    },
    set: (target, key, newValue, receiver) => {
      const res = Reflect.set(target, key, newValue, receiver)

      // 触发依赖
      trigger(target, TrackOpTypes.set, key, newValue)

      return res
    }
  })
  proxyMap.set(target, proxy)
  return proxy
}

const Vue1 = { reactive: reactiveImpl, effect: effectImpl, triggerEffect, trackEffect, ReactiveEffect }
